package data
{
	import com.maclema.mysql.MySqlToken;
	
	import controls.DataHub;
	import controls.events.ServiceEvent;
	import controls.throbber.ThrobberManager;
	
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	import mx.utils.StringUtil;
	
	[Event(name="degreeRemoved", source="controls.events.ServiceEvent")]
	[Event(name="degreeAdded", source="controls.events.ServiceEvent")]
	
	public class User extends EventDispatcher
	{
		public var username:String;
		public var password:String;
		
		private var _primaryMajor:Degree;
		
		public var degrees:Dictionary;
		public var coursesTaken:Dictionary;
		
		private var courseIds:Array;
		private var hub:DataHub;
		
		public var coursesLoaded:Boolean;
		
		private var throbber:ThrobberManager = ThrobberManager.instance;
		
		public function User(userData:Object = null)
		{
			hub = DataHub.instance;
			
			coursesLoaded = false;
			coursesTaken = new Dictionary();
			degrees = new Dictionary();
			degrees[DegreeType.MAJOR] = new Dictionary();
			degrees[DegreeType.MINOR] = new Dictionary();
			degrees[DegreeType.CERTIFICATE] = new Dictionary();
			
			if(userData != null)
			{
				username = userData.userName;
				password = userData.password;
				
				userData.degree = StringUtil.trim(userData.degree);
				var degreeIds:Array = String(userData.degree).split(" ");
				
				userData.courseWork = StringUtil.trim(userData.courseWork);
				courseIds = String(userData.courseWork).split(" ");
				
				for each(var degreeId:int in degreeIds)
				{
					var degree:Degree = hub.getDegreeById(degreeId, onDegreeLoaded);
					if(degree != null)
					{
						if(degree.totalCredits == 0)
						{
							degree.addEventListener(ServiceEvent.PROGRAM_LOADED, onDegreeInfoLoaded);
							degree.getClassInformation();	
						}	
						else
							checkDegreeForTakenCourses(degree);
						degrees[degree.type][degree.name] = degree;
					}
				}
			}
		}
		
		private function checkDegreeForTakenCourses(degree:Degree):void
		{
			var oldIds:Array = [];
			for each(var courseId:int in courseIds)
			{
				var course:Course = degree.hasCourseById(courseId);
				if(course != null)
					this.coursesTaken[course.title] = course;
				else
					oldIds.push(courseId);
			}
			courseIds = oldIds;
			
			if(courseIds.length == 0)
			{
				this.dispatchEvent(new ServiceEvent(ServiceEvent.USER_COURSES_LOADED, this));
				coursesLoaded = true;
			}
		}
		
		// --------------- Event Handling ---------------- //
		private function onDegreeLoaded(sqlData:Object, token:MySqlToken):void
		{
			checkDegreeForTakenCourses(token.degree);
		}
		
		private function onDegreeInfoLoaded(evt:ServiceEvent):void
		{	
			var degree:Degree = evt.data as Degree;
			degrees[degree.type][degree.name] = degree;
			checkDegreeForTakenCourses(degree);
			throbber.stopThrobber('2');
			this.dispatchEvent(new ServiceEvent(ServiceEvent.USER_DEGREE_ADDED, degree));
		}
		// --------------- END Event Handling ---------------- //
		
		// ---------------- External Functions ----------------- //	
		public function getCreditsForDegree(degree:Degree):int
		{
			var degreeCredits:int = 0;
			for each(var course:Course in this.coursesTaken)
			{
				if(degree.hasCourseById(course.id))
					degreeCredits += course.credits;
			}
			return degreeCredits;
		}
		
		public function get credits():int
		{
			var credits:int = 0;
			for each(var course:Course in coursesTaken)
				credits += course.credits;
			return credits;
		}
		
		public function canTake(course:Course):Boolean
		{
			var pre:Boolean = false;
			var co:Boolean = false;
			
			if(course.coreq == 0 && course.prereq == 0)
				return true;
			if(course.prereq != 0 && hasTakenCourseById(course.prereq)
				&& course.coreq != 0 && hasTakenCourseById(course.coreq))
				return true;
			if(course.prereq != 0 && hasTakenCourseById(course.prereq))
				return true;
			if(course.coreq != 0 && hasTakenCourseById(course.coreq))
				return true;
			return false;
		}
		
		public function hasTakenCourse(course:Course):Boolean
		{
			for each(var c:Course in this.coursesTaken)
				if(c == course)
					return true;
			return false;
		}
		
		public function hasTakenCourseById(courseId:int):Boolean
		{
			for each(var c:Course in this.coursesTaken)
				if(c.id == courseId)
					return true;
			return false;
		}
		
		public function fulfillsGroupRequirement(group:ClassGroup):Boolean
		{
			var takenCredits:int = 0;
			for each(var course:Course in group.classes)
			{
				if(this.getTakenCourse(course.title) != null)
					takenCredits += course.credits;
			}
			
			if(takenCredits >= group.creditsRequired)
				return true;
			return false;
		}
		
		public function hasDegree(degree:Degree):Boolean
		{
			return degrees[degree.type].hasOwnProperty(degree.name);
		}
		
		public function updateDegree(degree:Degree):void
		{
			degrees[degree.type][degree.name] = degree;
			//this.dispatchEvent(new ServiceEvent(ServiceEvent.USER_DEGREE_ADDED, degree));
		}
		
		public function removeDegree(degree:Degree):void
		{
			degrees[degree.type][degree.name] = null;
			this.dispatchEvent(new ServiceEvent(ServiceEvent.USER_DEGREE_REMOVED, degree));
		}
		
		public function getDegrees():Array
		{
			var array:Array = [];
			for (var degreeType:String in degrees)
				for each(var degree:Degree in degrees[degreeType])
					array.push(degree);			
			return array;
		}
			
		public function addDegree(degree:Degree):void
		{
			throbber.startThrobber('2');
			degrees[degree.type][degree.name] = degree;
			degree.addEventListener(ServiceEvent.PROGRAM_LOADED, onDegreeInfoLoaded);
			degree.getClassInformation();
		}
		
		public function addTakenCourse(course:Course):void
		{
			coursesTaken[course.title] = course;
			this.dispatchEvent(new ServiceEvent(ServiceEvent.USER_COURSE_ADDED, course));
		}
		
		public function getTakenCourse(title:String):Course
		{
			if(coursesTaken.hasOwnProperty(title))
				return coursesTaken[title];
			return null;
		}
		// ---------------- END External Functions ----------------- //
	}
}